<?php

/**
* 模板引擎基础类
*/
class TemplateModule
{
    private $config = array(
        'suffix' => '.html',      // 设置模板文件的后缀
        'templateDir' => 'template/',    // 设置模板所在的文件夹
        'compileDir' => 'cache/',    // 设置编译后存放的目录
		'autotplDir' => 'autotpl/',    // 设置自动生成模板存放的目录
        'cache_html' => true,    // 是否需要编译成静态的HTML文件
        'suffix_cache' => '.html',    // 设置编译文件的后缀
        'cache_time' => 7200,    //  多长时间自动更新，单位秒
        'php_turn' => true,   // 是否支持原生PHP代码
        'cache_control' => 'control.dat',
        'debug' => false,
    );
    private static $instance = null;
    private $value = array();   // 值栈
    private $compileTool;   // 编译器
    public $file;     // 模板文件名，不带路径
	public $content;  // 内容HTML
    public $debug = array();   // 调试信息
    private $controlData = array();
    public function __construct($config = array())
    {
        $this->debug['begin'] = microtime(true);
        $this->config = $config + $this->config;
        if (!is_dir($this->config['templateDir'])) {
            return array('error'=>1,'msg'=>"模板目录不存在！");
        }
        if (!is_dir($this->config['compileDir'])) {
            mkdir($this->config['compileDir'], 0770, true);
        }
		if (!is_dir($this->config['autotplDir'])) {
            mkdir($this->config['autotplDir'], 0770, true);
        }
        $this->getPath();
    }
    /**
    *获取绝对路径
    */
    public function getPath() {
        $this->config['templateDir'] = strtr(realpath($this->config['templateDir']), '\\', '/').'/';
        $this->config['compileDir'] = strtr(realpath($this->config['compileDir']), '\\', '/').'/';
    }
    /**
    *取得模板引擎的实例
    */
    public static function getInstance() {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    /* 设置模板引擎参数 */
    public function setConfig($key, $value = null) {
        if (is_array($key)) {
            $this->config = $key + $this->config;
        }else {
            $this->config[$key] = $value;
        }
    }
    /* 获取当前模板引擎配置，仅供调试使用 */
    public function getConfig($key = null) {
        if ($key) {
            return $this->config[$key];
        }else {
            return $this->config;
        }
    }
    /**
    *注入单个变量
    */
    public function assign($key, $value) {
        $this->value[$key] = $value;
    }
    /**
    *注入数组变量
    */
    public function assignArray($array) {
        if (is_array($array)) {
            foreach($array as $k => $v) {
                $this->value[$k] = $v;
            }
        }
    }
    /**
     * 获取模板文件完整路径
     */
    public function path() {
		$fileurl = $this->config['templateDir'].$this->file.$this->config['suffix'];
		if ($this->content)$fileurl = $this->config['autotplDir'].$this->file.$this->config['suffix'];
        return $fileurl;
    }
    /**
    *判断是否开启了缓存
    */
    public function needCache() {
        return $this->config['cache_html'];
    }
    /**
    *是否需要重新生成静态文件
    */
    public function reCache($file) {
        $flag = true;
        $cacheFile = $this->config['compileDir'].md5($file).$this->config['suffix_cache'];
        if ($this->config['cache_html'] === true) {
            $timeFlag = (time() - @filemtime($cacheFile)) < $this->config['cache_time'] ? true : false;
            if (is_file($cacheFile) && filesize($cacheFile) > 1 && $timeFlag && filemtime($compileFile) >= filemtime($this->path())) {
                $flag = false;
            }else {
                $flag = true;
            }
        }
        return $flag;
    }
    /**
    *显示模板
	** ob_get_contents() - 返回输出缓冲区的内容
	** ob_flush() - 冲刷出（送出）输出缓冲区中的内容
	** ob_clean() - 清空（擦掉）输出缓冲区
	** ob_end_flush() - 冲刷出（送出）输出缓冲区内容并关闭缓冲
	** ob_end_clean() - 清空（擦除）缓冲区并关闭输出缓冲
	** flush() - 刷新输出缓冲　
	** 通常是ob_flush();flush()同时一起使用
	** 使用ob_start()把输出那同输出到缓冲区，而不是到浏览器。
	** 然后用ob_get_contents得到缓冲区的数据。
	** ob_start()在服务器打开一个缓冲区来保存所有的输出。所以在任何时候使用echo ，输出都将被加入缓冲区中，直到程序运行结束或者使用ob_flush()来结束。然后在服务器中缓冲区的内容才会发送到浏览器，由浏览器来解析显示。
	** 函数ob_end_clean 会清除缓冲区的内容，并将缓冲区关闭，但不会输出内容。
	** 此时得用一个函数ob_get_contents()在ob_end_clean()前面来获得缓冲区的内容。
	** 这样的话， 能将在执行ob_end_clean()前把内容保存到一个变量中，然后在ob_end_clean()后面对这个变量做操作。
    */
    public function show($file, $htmlcontent='') {
        $this->file = $file;
		$this->content = $htmlcontent;
		if (!is_dir($this->config['compileDir'])) {
			mkdir($this->config['compileDir'], 0770, true);
		}
		if($this->content)
		{
			if (!is_dir($this->config['autotplDir'])) {
				mkdir($this->config['autotplDir'], 0770, true);
			}
			if ($this->content != file_get_contents($this->path()))file_put_contents($this->path(), $this->content);
		}
        if (!is_file($this->path())) {
			return array('error'=>1,'msg'=>"找不到对应的模板！");
        }
        $compileFile = $this->config['compileDir'].md5($file).'.php';
        $cacheFile = $this->config['compileDir'].md5($file).$this->config['suffix_cache'];
        extract($this->value, EXTR_OVERWRITE);
        if ($this->config['cache_html'] === true) { // 开启缓存的处理逻辑
            if ($this->reCache($file) === true) { // 需要更新缓存的处理逻辑
                $this->debug['cached'] = 'false';
                $this->compileTool = new CompileModule($this->path(), $compileFile, $this->config);
                if ($this->needCache()) {ob_start();} // 打开输出控制缓冲
                if (!is_file($compileFile) || filemtime($compileFile) < filemtime($this->path())) {
                    $this->compileTool->value = $this->value;
                    $this->compileTool->compile();
                    include $compileFile;
                }else {
                    include $compileFile;
                }
				$message = ob_get_contents(); // 获取输出缓冲中的内容（在include编译文件的时候就有输出了）
                ob_end_clean(); 
				if ($this->needCache()) {
                    file_put_contents($cacheFile, $message);
                }
				
				return array('error'=>0, 'datacontent'=>$message); 
            }else {
                readfile($cacheFile);
                $this->debug['cached'] = 'true';
				$message = ob_get_contents();
				ob_end_clean(); 
				
				return array('error'=>0, 'datacontent'=>$message); 
            }
        }else {
            if (!is_file($compileFile) || filemtime($compileFile) < filemtime($this->path())) {
                $this->compileTool = new CompileModule($this->path(), $compileFile, $this->config);
                $this->compileTool->value = $this->value;
                $this->compileTool->compile();
                include $compileFile;
				$message = ob_get_contents();
				ob_end_clean(); 
				
				return array('error'=>0, 'datacontent'=>$message); 
            }else {
                include $compileFile;
				$message = ob_get_contents();
				ob_end_clean(); 
				
				return array('error'=>0, 'datacontent'=>$message); 
            }
        }
        $this->debug['spend'] = microtime(true) - $this->debug['begin'];
        $this->debug['count'] = count($this->value);
        //$this->debug_info();
    }
    public function debug_info() {
        if ($this->config['debug'] === true) {
            echo PHP_EOL, '---------debug info---------', PHP_EOL;
            echo "程序运行日期：", date("Y-m-d H:i:s"), PHP_EOL;
            echo "模板解析耗时：", $this->debug['spend'], '秒', PHP_EOL;
            echo '模板包含标签数目：', $this->debug['count'], PHP_EOL;
            echo '是否使用静态缓存：', $this->debug['cached'], PHP_EOL;
            echo '模板引擎实例参数：', var_dump($this->getConfig());
        }
    }
    /**
    *清理缓存的HTML文件
    */
    public function clean($path = null) {
        if ($path === null) {
            $path = $this->config['compileDir'];
            $path = glob($path.'* '.$this->config['suffix_cache']);
        }else {
            $path = $this->config['compileDir'].md5($path).$this->config['suffix_cache'];
        }
        foreach((array)$path as $v) {
            unlink($v);
        }
    }
}

?>